package com.yzion.baselib.http

import com.blankj.utilcode.util.ThreadUtils
import com.yzion.baselib.http.lifecycle.unbindToLifeCycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Response
import retrofit2.awaitResponse

/**
 * 接口基类，项目创建接口类时继承该类，实现getService()，getBaseUrl()，getHttpConfig()从而快速创建接口类，
 * 一般接口类可按照功能模块划分，创建不同的接口类
 * 例如项目中登录接口
 */
/*
示例代码
interface LoginApiService {

    @JvmSuppressWildcards
    @POST
    fun loginPhonePwd(
            @Body params: Map<String, Any?>,
            @Url url: String = ApiUrl.LOGIN_PHONE_PWD_URL
    ): Call<ApiResponse<LoginInfoBean>>

}
*/

/*
示例代码
object LoginApi : BaseApi<LoginApiService>() {

    override fun getService(): LoginApiService {
        return LoginApiService::class.java
    }

    override fun getBaseUrl(): String {
        return BuildConfig.API_URL
    }

    override fun getHttpConfig(): HttpConfig? {
        return ApiConfig()
    }

    //手机账号密码登陆（异步方式）
    fun loginPhonePwd(
        phone: String,
        password: String,
        callback: Callback<ApiResponse<LoginInfoBean>>?
    ): Call<*> {
        val params = HashMap<String, Any?>()
        params["username"] = phone
        params["password"] = password
        return handleResponseAsync(callback, apiService.loginPhonePwd(params))
    }

    //手机账号密码登陆（同步方式）
    suspend fun loginPhonePwd(
        phone: String,
        password: String
    ): Response<ApiResponse<LoginInfoBean>> {
        val params = HashMap<String, Any?>()
        params["username"] = phone
        params["password"] = password
        return apiService.loginPhonePwd(params).awaitResponse()
    }
}
*/

/*
示例代码
class ApiConfig : HttpConfig() {

    override fun configLogEnable(): Boolean {
        return true
    }

    override fun configResponseSuccessCode(): String {
        return "0000"
    }

    override fun configOkHttpClient(builder: OkHttpClient.Builder) {
        builder.sslSocketFactory(SSLConfig.sSLSocketFactory, SSLConfig.trustManager[0])
                .hostnameVerifier(SSLConfig.hostnameVerifier)
    }

    override fun configInterceptors(): MutableList<Interceptor> {
        return ArrayList<Interceptor>().apply {//添加拦截器
//            add(RequestHandlerInterceptor(HeaderHandler()))//请求头统一处理
//            add(RequestHandlerInterceptor(EncryptHandler()))//请求数据加密，响应数据解密
        }
    }

}
*/

abstract class BaseApi<S> {

    /**
     * 接口类实例，可以直接调用接口方法
     */
//    val apiService: S by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
//        createApiService()
//    }

    //这样可以把apiService，下次使用会重新创建对象，这样可以切换API的请求域名，域名根据getBaseUrl()获取
    var apiService: S? = null
        get() {
            return if (field == null) {
                val api = createApiService()
                field = api
                api
            } else {
                field
            }
        }


    /**
     * 创建apiService，子类可使用apiService.method()来调用接口方法
     */
    open fun createApiService(): S {
        if (getHttpConfig() == null) {
            return ApiHelper.get().create(getBaseUrl(), getService())
        }
        return ApiHelper.get().create(getBaseUrl(), getService(), getHttpConfig()!!)
    }

    /**
     * 返回定义接口的服务，例如：LoginApiService::Class.java
     */
    abstract fun getService(): Class<S>

    /**
     * 返回接口的地址，例如：https://api.yzion.site
     */
    abstract fun getBaseUrl(): String

    /**
     * 返回Http网络配置，如果返回null，则使用默认配置
     * 一般项目需一个自定义的配置类继承HttpConfig
     */
    abstract fun getHttpConfig(): BaseApiConfig?

    /**
     * 如果服务器请求返回失败的代码，则创建服务器自定义异常类ApiResponseException
     */
    open fun <T : BaseResponse<*>> createResponseException(response: Response<T>): Throwable {
        if (response.body() != null) {
            return ApiResponseException(response.body()!!.code(), response.body()!!.message())
        }
        return ApiResponseException((-1).toString(), "unknown")
    }

    /**
     * 判断请求是否成功，亦可使用Response<BaseResponse<T>>.isSuccess()的扩展方法
     * 1.http状态成功，if the code is in [200..300), which means the request was successfully received,
     * understood, and accepted.
     * 2.服务器请求返回成功，该成功的代码是服务器自定义的，需要在HttpConfig中的configResponseSuccessCode方法中定义
     */
    open fun <T : BaseResponse<*>> isSuccessful(response: Response<T>): Boolean {
        try {
            if (response.isSuccessful
                && response.body()?.code() == getHttpConfig()?.configResponseSuccessCode()
            ) {
                return true
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return false
    }

    /**
     * 协程异步方式处理网络请求，网络异常会回调失败接口（Callback<T>.onFailure(throwable: Throwable)），
     * throwable为网络异常信息（例如：SocketTimeoutException），
     * 如果是服务器返回异常，throwable为ApiResponseException封装的异常信息，
     * 回调失败时可以通过throwable is ApiResponseException来判断是否属于业务异常，
     * 否则属于网络等其他异常
     */
    fun <T : BaseResponse<*>> handleResponseAsync(
        callback: Callback<T>?,
        call: Call<T>
    ): Call<*> {
        service.launch {
            try {
                val response = call.awaitResponse()
                if (isSuccessful(response)) {
                    callSuccess(callback, response.body()!!)
                } else {
                    //这里是服务器异常
                    callFailure(callback, createResponseException(response))
                }
            } catch (e: Exception) {
                e.printStackTrace()
                callFailure(callback, e)//这里一般是网络异常
            }
            call.unbindToLifeCycle()//从生命周期中移除观察者
        }
        return call
    }

    fun <T : BaseResponse<*>> callFailure(callback: Callback<T>?, e: Throwable) {
        ThreadUtils.runOnUiThread {
            callback?.onFailure(e)
        }
    }

    fun <T : BaseResponse<*>> callSuccess(callback: Callback<T>?, t: T) {
        ThreadUtils.runOnUiThread {
            callback?.onSuccess(t)
        }
    }

    companion object {
        val service: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    }

    interface Callback<T : BaseResponse<*>> {
        fun onSuccess(data: T?)
        fun onFailure(throwable: Throwable) {}
    }

}